<template>
  <div class="snip-root" v-if="active">
    <canvas ref="bgCanvas" class="snip-bg-canvas" :width="screenW" :height="screenH"></canvas>
    <canvas ref="selectCanvas" class="snip-select-canvas" :width="screenW" :height="screenH"
      @mousedown="onDown" @mousemove="onMove" @mouseup="onUp" @mouseleave="onUp"></canvas>
    <div v-if="showToolbar" class="snip-toolbar" :style="toolbarStyle">
      <button @click="saveImage">保存</button>
      <button @click="copyImage">复制</button>
      <button @click="reset">重新截图</button>
      <button @click="cancel">取消</button>
    </div>
    <div v-if="previewUrl" class="snip-preview">
      <img :src="previewUrl" alt="截图预览" />
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
import html2canvas from 'html2canvas'

const active = ref(true)
const screenW = window.innerWidth
const screenH = window.innerHeight
const bgCanvas = ref(null)
const selectCanvas = ref(null)
const ctxBg = ref(null)
const ctxSel = ref(null)

const selecting = ref(false)
const moving = ref(false)
const start = reactive({x:0, y:0})
const end = reactive({x:0, y:0})
const rect = reactive({x:0, y:0, w:0, h:0})
const dragOffset = reactive({x:0, y:0})
const showToolbar = ref(false)
const previewUrl = ref('')
const screenshotImg = ref(null)

const toolbarStyle = computed(()=>({
  left: rect.x + rect.w - 120 + 'px',
  top: rect.y + rect.h + 10 + 'px'
}))

onMounted(async () => {
  ctxBg.value = bgCanvas.value.getContext('2d')
  ctxSel.value = selectCanvas.value.getContext('2d')
  // 只截一次
  const canvas = await html2canvas(document.body, {backgroundColor: null, useCORS: true})
  screenshotImg.value = new window.Image()
  screenshotImg.value.src = canvas.toDataURL('image/png')
  screenshotImg.value.onload = () => {
    ctxBg.value.drawImage(screenshotImg.value, 0, 0, screenW, screenH)
    ctxBg.value.save()
    ctxBg.value.fillStyle = 'rgba(0,0,0,0.25)'
    ctxBg.value.fillRect(0,0,screenW,screenH)
    ctxBg.value.restore()
    drawSelect()
  }
})

function drawBg() {
  ctxBg.value.clearRect(0,0,screenW,screenH)
  // 只画半透明遮罩
  ctxBg.value.save()
  ctxBg.value.fillStyle = 'rgba(0,0,0,0.25)'
  ctxBg.value.fillRect(0,0,screenW,screenH)
  ctxBg.value.restore()
}

function drawSelect() {
  ctxSel.value.clearRect(0,0,screenW,screenH)
  if (rect.w && rect.h) {
    ctxSel.value.save()
    ctxSel.value.strokeStyle = '#0078d7'
    ctxSel.value.lineWidth = 2
    ctxSel.value.setLineDash([6,4])
    ctxSel.value.strokeRect(rect.x, rect.y, rect.w, rect.h)
    ctxSel.value.setLineDash([])
    ctxSel.value.restore()
    // 半透明遮罩外部
    ctxSel.value.save()
    ctxSel.value.globalCompositeOperation = 'destination-out'
    ctxSel.value.fillRect(rect.x, rect.y, rect.w, rect.h)
    ctxSel.value.restore()
  }
}

function onDown(e) {
  const x = e.offsetX, y = e.offsetY
  if (rect.w && rect.h && x>rect.x && x<rect.x+rect.w && y>rect.y && y<rect.y+rect.h) {
    moving.value = true
    dragOffset.x = x - rect.x
    dragOffset.y = y - rect.y
  } else {
    selecting.value = true
    start.x = x; start.y = y
    end.x = x; end.y = y
    rect.x = x; rect.y = y; rect.w = 0; rect.h = 0
    showToolbar.value = false
    previewUrl.value = ''
  }
}
function onMove(e) {
  const x = e.offsetX, y = e.offsetY
  if (selecting.value) {
    end.x = x; end.y = y
    rect.x = Math.min(start.x, end.x)
    rect.y = Math.min(start.y, end.y)
    rect.w = Math.abs(end.x - start.x)
    rect.h = Math.abs(end.y - start.y)
    drawBg(); drawSelect()
  } else if (moving.value) {
    rect.x = x - dragOffset.x
    rect.y = y - dragOffset.y
    drawBg(); drawSelect()
  }
}
function onUp(e) {
  if (selecting.value || moving.value) {
    selecting.value = false
    moving.value = false
    drawBg(); drawSelect()
    if (rect.w>10 && rect.h>10) {
      showToolbar.value = true
      genPreview()
    }
  }
}
function genPreview() {
  if (!screenshotImg.value) return;
  const temp = document.createElement('canvas')
  temp.width = rect.w
  temp.height = rect.h
  const tctx = temp.getContext('2d')
  tctx.drawImage(screenshotImg.value, rect.x, rect.y, rect.w, rect.h, 0, 0, rect.w, rect.h)
  previewUrl.value = temp.toDataURL('image/png')
}
function saveImage() {
  if (!previewUrl.value) return
  const a = document.createElement('a')
  a.href = previewUrl.value
  a.download = 'snip.png'
  a.click()
}
async function copyImage() {
  if (!previewUrl.value) return
  const res = await fetch(previewUrl.value)
  const blob = await res.blob()
  await navigator.clipboard.write([
    new window.ClipboardItem({ 'image/png': blob })
  ])
  alert('已复制到剪贴板')
}
function reset() {
  rect.x=rect.y=rect.w=rect.h=0
  showToolbar.value = false
  previewUrl.value = ''
  drawBg(); drawSelect()
}
function cancel() {
  active.value = false
}
</script>

<style scoped lang="scss">
$primary: #0078d7;
$toolbar-bg: #f3f6fb;
$shadow: 0 2px 8px #0001;

.snip-root {
  position: fixed;
  left: 0; top: 0;
  width: 100vw;
  height: 100vh;
  z-index: 99999;
  .snip-bg-canvas, .snip-select-canvas {
    position: absolute;
    left: 0; top: 0;
    width: 100vw;
    height: 100vh;
    pointer-events: none;
  }
  .snip-select-canvas {
    pointer-events: auto;
    cursor: crosshair;
  }
  .snip-toolbar {
    position: absolute;
    display: flex;
    gap: 10px;
    background: $toolbar-bg;
    border-radius: 6px;
    box-shadow: $shadow;
    padding: 6px 18px;
    z-index: 10;
    button {
      background: none;
      border: 1px solid #ddd;
      border-radius: 4px;
      padding: 2px 12px;
      font-size: 15px;
      cursor: pointer;
      &:hover { background: #e3eefd; border-color: $primary; }
    }
  }
  .snip-preview {
    position: fixed;
    right: 24px;
    bottom: 24px;
    background: #fff;
    border: 1px solid #eee;
    border-radius: 8px;
    box-shadow: $shadow;
    padding: 8px;
    z-index: 20;
    img {
      max-width: 260px;
      max-height: 180px;
      display: block;
    }
  }
}
</style> 